from typing import Dict, Any, List
import os

class HTMLBuilder:
    """HTML报告构建器"""
    
    def __init__(self):
        self.styles = self._get_styles()
        self.scripts = self._get_scripts()
    
    def build_header(self) -> str:
        """构建HTML头部"""
        return f"""
<!DOCTYPE html>
<html>
<head>
    <title>端口扫描报告</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
{self.styles}
    </style>
</head>
<body>
    <div class="container">
        <h1>端口扫描报告</h1>
"""
    
    def build_summary(self, scan_info: Dict[str, Any], open_ports: int) -> str:
        """构建扫描摘要部分"""
        return f"""
        <div class="summary">
            <h2>扫描摘要</h2>
            <div class="stats">
                <div class="stat-card">
                    <h3>开始时间</h3>
                    <p>{scan_info['start_time']}</p>
                </div>
                <div class="stat-card">
                    <h3>结束时间</h3>
                    <p>{scan_info['end_time']}</p>
                </div>
                <div class="stat-card">
                    <h3>扫描主机数</h3>
                    <p>{scan_info['total_hosts']}</p>
                </div>
                <div class="stat-card">
                    <h3>扫描端口数</h3>
                    <p>{scan_info['total_ports']}</p>
                </div>
                <div class="stat-card">
                    <h3>开放端口数</h3>
                    <p>{open_ports}</p>
                </div>
            </div>
        </div>
"""
    
    def build_controls(self) -> str:
        """构建控制按钮部分"""
        return """
        <div class="action-controls">
            <div class="action-group">
                <button class="btn copy-btn" onclick="copyToClipboard()">
                    <i class="fas fa-copy"></i>复制报告
                </button>
            </div>
            <div class="action-group">
                <button class="btn filter-btn active" onclick="togglePortFilter('all')">
                    <i class="fas fa-list"></i>显示全部端口
                </button>
                <button class="btn filter-btn" onclick="togglePortFilter('open')">
                    <i class="fas fa-check-circle"></i>仅显示开放端口
                </button>
            </div>
        </div>
"""
    
    def build_table_header(self) -> str:
        """构建表格头部"""
        return """
        <table>
            <tr>
                <th>序号</th>
                <th>目标主机</th>
                <th>端口</th>
                <th>状态</th>
                <th>服务</th>
                <th>详细信息</th>
                <th>响应时间</th>
            </tr>
"""
    
    def build_table_row(self, index: int, result: Dict[str, Any], status_text: str,
                       status_class: str, service: str, latency: str,
                       details_html: str) -> str:
        """构建表格行"""
        return f"""
            <tr class="port-row">
                <td>{index}</td>
                <td>{result['target']}</td>
                <td>{result['port']}</td>
                <td><span class="status {status_class}">{status_text}</span></td>
                <td>{service}</td>
                <td class="service-details">{details_html}</td>
                <td>{latency}</td>
            </tr>"""
    
    def build_footer(self) -> str:
        """构建HTML尾部"""
        return f"""
            </table>
        </div>
        <script>
{self.scripts}
        </script>
    </body>
</html>
"""
    
    def _get_styles(self) -> str:
        """获取CSS样式"""
        return """
        :root {
            --primary-color: #2196F3;
            --success-color: #4CAF50;
            --warning-color: #FFC107;
            --danger-color: #F44336;
            --border-radius: 12px;
            --card-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            margin: 0;
            padding: 20px;
            background-color: #f0f2f5;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background-color: white;
            padding: 24px;
            border-radius: var(--border-radius);
            box-shadow: var(--card-shadow);
        }
        
        h1 {
            color: var(--primary-color);
            margin-bottom: 24px;
            padding-bottom: 12px;
            border-bottom: 2px solid var(--primary-color);
            font-size: 28px;
        }
        
        .summary {
            background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
            padding: 20px;
            border-radius: var(--border-radius);
            margin-bottom: 24px;
            box-shadow: var(--card-shadow);
        }
        
        .summary h2 {
            margin-top: 0;
            color: var(--primary-color);
            font-size: 22px;
            margin-bottom: 16px;
        }
        
        .stats {
            display: flex;
            flex-wrap: wrap;
            gap: 16px;
            margin-bottom: 20px;
        }
        
        .stat-card {
            flex: 1;
            min-width: 180px;
            background: white;
            padding: 16px;
            border-radius: var(--border-radius);
            box-shadow: var(--card-shadow);
            transition: transform 0.2s ease;
        }
        
        .stat-card:hover {
            transform: translateY(-2px);
        }
        
        .stat-card h3 {
            margin: 0;
            color: var(--primary-color);
            font-size: 16px;
            margin-bottom: 8px;
        }
        
        .stat-card p {
            margin: 0;
            font-size: 18px;
            font-weight: 500;
        }
        
        table {
            width: 100%;
            border-collapse: separate;
            border-spacing: 0;
            margin-bottom: 24px;
            border-radius: var(--border-radius);
            overflow: hidden;
            box-shadow: var(--card-shadow);
        }
        
        th, td {
            padding: 16px;
            text-align: left;
            border-bottom: 1px solid #eee;
        }
        
        th {
            background: linear-gradient(135deg, var(--primary-color) 0%, #1976D2 100%);
            color: white;
            font-weight: 500;
        }
        
        tr:last-child td {
            border-bottom: none;
        }
        
        tr:nth-child(even) {
            background-color: #f8f9fa;
        }
        
        tr:hover {
            background-color: #f5f5f5;
        }
        
        .status {
            font-weight: 500;
            padding: 6px 12px;
            border-radius: 20px;
            display: inline-block;
            min-width: 80px;
            text-align: center;
        }
        
        .status.open { 
            background: linear-gradient(135deg, var(--success-color) 0%, #388E3C 100%);
            color: white;
        }
        
        .status.closed { 
            background: linear-gradient(135deg, var(--danger-color) 0%, #D32F2F 100%);
            color: white;
        }
        
        .status.filtered { 
            background: linear-gradient(135deg, var(--warning-color) 0%, #FFA000 100%);
            color: black;
        }
        
        .service-details {
            margin-top: 8px;
            font-size: 14px;
            color: #666;
            line-height: 1.8;
        }
        
        .action-controls {
            display: flex;
            align-items: center;
            gap: 16px;
            margin-bottom: 24px;
            padding: 16px;
            background: white;
            border-radius: var(--border-radius);
            box-shadow: var(--card-shadow);
        }
        
        .action-group {
            display: flex;
            gap: 12px;
            align-items: center;
        }
        
        .action-group:not(:last-child) {
            padding-right: 16px;
            border-right: 2px solid #eee;
        }
        
        .btn {
            border: none;
            padding: 10px 20px;
            border-radius: var(--border-radius);
            cursor: pointer;
            font-weight: 500;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 14px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        
        .btn i {
            font-size: 16px;
        }
        
        .copy-btn {
            background: linear-gradient(135deg, var(--primary-color) 0%, #1976D2 100%);
            color: white;
        }
        
        .copy-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(33,150,243,0.3);
        }
        
        .filter-btn {
            background: #f5f5f5;
            color: #333;
        }
        
        .filter-btn:hover {
            background: #e0e0e0;
            transform: translateY(-1px);
        }
        
        .filter-btn.active {
            background: linear-gradient(135deg, var(--primary-color) 0%, #1976D2 100%);
            color: white;
        }
        
        .http-link {
            color: var(--primary-color);
            text-decoration: none;
            padding: 4px 12px;
            border-radius: 4px;
            background: #e3f2fd;
            transition: all 0.3s ease;
            display: inline-block;
            margin-top: 4px;
        }
        
        .http-link:hover {
            background: #bbdefb;
            text-decoration: none;
            transform: translateY(-1px);
        }
        
        @media (max-width: 768px) {
            .stats {
                flex-direction: column;
            }
            
            .stat-card {
                width: 100%;
            }
            
            .action-controls {
                flex-direction: column;
            }
            
            .action-group {
                width: 100%;
                justify-content: center;
                padding: 10px 0;
            }
            
            .action-group:not(:last-child) {
                border-right: none;
                border-bottom: 2px solid #eee;
            }
            
            table {
                display: block;
                overflow-x: auto;
            }
        }
"""
    
    def _get_scripts(self) -> str:
        """获取JavaScript脚本"""
        return """
        // 添加端口过滤功能
        function togglePortFilter(filter) {
            // 更新按钮状态
            const buttons = document.querySelectorAll('.filter-btn');
            buttons.forEach(btn => {
                btn.classList.remove('active');
                if (btn.textContent.includes(filter === 'all' ? '全部' : '开放')) {
                    btn.classList.add('active');
                }
            });
            
            // 获取所有端口行
            const rows = document.querySelectorAll('table tr:not(:first-child)');
            let visibleCount = 0;
            
            rows.forEach(row => {
                const status = row.querySelector('.status').textContent;
                if (filter === 'all') {
                    row.style.display = '';
                    visibleCount++;
                } else {
                    // 检查端口状态是否为"开放"
                    if (status === '开放') {
                        row.style.display = '';
                        visibleCount++;
                    } else {
                        row.style.display = 'none';
                    }
                }
            });
            
            // 更新序号
            updateRowNumbers();
        }
        
        // 更新行号
        function updateRowNumbers() {
            let number = 1;
            const rows = document.querySelectorAll('table tr:not(:first-child)');
            rows.forEach(row => {
                if (row.style.display !== 'none') {
                    row.querySelector('td:first-child').textContent = number++;
                }
            });
        }
        
        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', () => {
            updateRowNumbers();
        });
        
        // 复制功能
        function copyToClipboard() {
            const rows = document.querySelectorAll('table tr');
            let text = '端口扫描报告\\n\\n';
            
            // Add summary
            const startTime = document.querySelector('.stats .stat-card:nth-child(1) p').textContent;
            const endTime = document.querySelector('.stats .stat-card:nth-child(2) p').textContent;
            const totalHosts = document.querySelector('.stats .stat-card:nth-child(3) p').textContent;
            const totalPorts = document.querySelector('.stats .stat-card:nth-child(4) p').textContent;
            const openPorts = document.querySelector('.stats .stat-card:nth-child(5) p').textContent;
            
            text += `扫描开始时间: ${startTime}\\n`;
            text += `扫描结束时间: ${endTime}\\n`;
            text += `扫描主机数: ${totalHosts}\\n`;
            text += `扫描端口数: ${totalPorts}\\n`;
            text += `开放端口数: ${openPorts}\\n\\n`;
            
            // Add table contents (only visible rows)
            rows.forEach((row, index) => {
                if (index === 0 || row.style.display === 'none') return; // Skip header row and hidden rows
                const cells = row.querySelectorAll('td');
                const target = cells[1].textContent;
                const port = cells[2].textContent;
                const status = cells[3].textContent;
                const service = cells[4].textContent;
                const details = cells[5].textContent;
                const latency = cells[6].textContent;
                
                text += `${target}:${port} - ${status} (${service})\\n`;
                if (details !== '无详细信息') {
                    text += `  详细信息: ${details}\\n`;
                }
                text += `  响应时间: ${latency}\\n`;
            });
            
            navigator.clipboard.writeText(text).then(() => {
                const btn = document.querySelector('.copy-btn');
                btn.innerHTML = '<i class="fas fa-check"></i>已复制';
                setTimeout(() => {
                    btn.innerHTML = '<i class="fas fa-copy"></i>复制报告';
                }, 2000);
            });
        }
""" 